package com.homedo.distributed.id.client.gen.buffer;

import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken;
import com.homedo.common.util.Detect;
import com.homedo.common.util.http.AsynMicroHttpUtil;
import com.homedo.distributed.id.client.gen.cache.GenApiCache;
import com.homedo.distributed.id.client.gen.constants.GenExecutorConstants;
import com.netflix.config.DynamicIntProperty;
import com.netflix.config.DynamicPropertyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

public abstract class GenBuffer<T> {
    public static final String DEFAULT_ERROR_ID = "_DEFAULT_ERROR_ID_";


    private static final Logger _LOGGER = LoggerFactory.getLogger(GenBuffer.class);

    private Gen<T>[] gens = new Gen[]{new Gen(), new Gen()};//双buffer

    private static final int ACTIVITY_PRE = 0;
    private static final int ACTIVITY_NEXT = 1;

    private volatile boolean initOK = false;//是否初始化

    //    private ReentrantLock lock = new ReentrantLock();
    //    private Condition acqCond = lock.newCondition();
    private Object monitor = new Object();//监控器
    //获取gen重试次数
    private static DynamicIntProperty RETRY_COUNT = DynamicPropertyFactory.getInstance().getIntProperty("gen.retryCount", 10);

    private ExecutorService updateBufferService = GenExecutorConstants.UPDATE_BUFFER_EXECUTOR;//更新buffer执行器

    private volatile boolean forceFetch;//当前是否强制拉取

    private GenMonitor genMonitor;

    private AtomicReference<Integer> activity = new AtomicReference<>(ACTIVITY_PRE);//当前使用的buffer

    private DynamicIntProperty cacheSize = new DynamicIntProperty(this.getCacheSizeKey(), 1000);//配置缓存条数

    public GenBuffer() {
        genMonitor = new GenMonitor(this);
    }

    //业务标志
    protected abstract String getBizTag();

    //生成的Gen类型
    protected abstract int getGenType();

    //缓存的个数
    public void setCacheSize(int cacheSize) {
        if (cacheSize < 500) {
            throw new IllegalArgumentException("[GEN初始化大小不得小于500]");
        }
        this.cacheSize = new DynamicIntProperty(this.getCacheSizeKey(), cacheSize);
//        ConcurrentCompositeConfiguration myConfiguration =
//                (ConcurrentCompositeConfiguration) DynamicPropertyFactory.getInstance().getBackingConfigurationSource();
//        myConfiguration.setOverrideProperty(this.getCacheSizeKey(), cacheSize);
    }

    public int getCacheSize() {
        return cacheSize.get();
    }


    public void init() {
        synchronized (monitor) {
            if (this.initOK) {
                return;
            }

            this.fetchGensAndDeal(ACTIVITY_PRE, false);
            if (gens[ACTIVITY_PRE].isReady()) {
                gens[ACTIVITY_PRE].setRunning(true);//设置为当前使用中
                this.initOK = true;
                genMonitor.start();
                _LOGGER.info("初始化GEN成功:{}#{}", this.getBizTag(), this.getGenType());
            }
        }

    }

    public T acquire() {

        this.checkInit();

        genMonitor.plusMonitorCount();//使用次数自增 不论成功或失败都应自增

        int retry = 0;

        return acquire(retry);

    }

    private T acquire(int retry) {

        int curActivity = activity.get();

        Gen<T> curBuf = gens[curActivity];

        _LOGGER.debug("CurBufferSize:{},NextBufferSize:{}", curBuf.size(), gens[curActivity == ACTIVITY_PRE ? ACTIVITY_NEXT : ACTIVITY_PRE].size());

        T ele = curBuf.gen();

        this.prepareNextReady(curBuf, curActivity == ACTIVITY_PRE ? ACTIVITY_NEXT : ACTIVITY_PRE);//准备下一个buffer

        if (ele == null) {//当前buffer为空时，则切换到下一个
            int nextActivity = curActivity == ACTIVITY_PRE ? ACTIVITY_NEXT : ACTIVITY_PRE;
            Boolean changed = this.tryChangeBuf(curActivity, nextActivity);//切换

            if (changed != null) {//不为null表示下一个buffer已准备好
                curActivity = nextActivity;

                curBuf = gens[curActivity];

                ele = curBuf.gen();//切换之后马上取出一条
            }
        }


        if (ele == null) {//之后还是null

            List fetchGens = this.fetchGens(1);
            if (Detect.notEmpty(fetchGens)) {
                ele = (T) fetchGens.get(0);//兼容获取
            } else {
                if (++retry > RETRY_COUNT.get()) {//兼容获取失败即重试
                    return (T) DEFAULT_ERROR_ID;
                } else {
                    try {
                        TimeUnit.SECONDS.sleep(1);//强制休眠1秒后再次尝试
                    } catch (InterruptedException e) {
                        //可忽略
                    }
                    return this.acquire(retry);
                }

            }

        }

        return ele;
    }

    private void prepareNextReady(Gen<T> curBuf, int nextActivity) {
        Gen<T> nextBuf = gens[nextActivity];
        if (!nextBuf.isReady() && (double) curBuf.getUsed().get() >= (double) curBuf.getCapacity() * 0.1D) {//当前buffer已用10% 且下一个buffer没有准备好
            this.updateBufferService.execute(new Runnable() {
                public void run() {
                    synchronized (monitor) {
                        if (!nextBuf.isReady()) {
                            fetchGensAndDeal(nextActivity, false);
                        }
                    }
                }
            });
        }
    }

    private String getCacheSizeKey() {
        return this.getClass().getSimpleName() + ".cacheSize." + this.getBizTag();
    }

    private void checkInit() {
        if (!this.initOK) {
            this.init();
        }
    }

    private Boolean tryChangeBuf(int curActivity, int nextActivity) {
        if (!gens[nextActivity].isReady() && gens[nextActivity].size() == 0) {//下一个buffer还没有准备好 就不能执行切换操作
            //兼容 即使下一个buffer还没有完全准备好 但是里面已经有可用的值了，即可尝试切过去
            return null;
        }

        boolean changed = this.compareAndSwap(curActivity, nextActivity);
        if (changed) {
            gens[curActivity].setReady(false);//设置被替换的buffer不可用
            gens[curActivity].setRunning(false);//设置被替换的buffer不在使用中

            gens[nextActivity].setRunning(true);
        }
        return changed;
    }

    private boolean compareAndSwap(int curActivity, int nextActivity) {
        return activity.compareAndSet(curActivity, nextActivity);
    }

    protected void fetchGensAndDeal(int act, boolean curForceFetch) {
        if (this.forceFetch && !curForceFetch) {//强制更新
            return;
        }
        synchronized (monitor) {
            if (this.forceFetch && !curForceFetch) {//强制更新
                return;
            }

            Gen<T> gen = gens[act];
            if (gen.size() > 0 && !curForceFetch) {//buffer中还有剩余不用获取
                return;
            }

            int cacheSize = this.getCacheSize();

            int fetchCounts = cacheSize / 1000 + (cacheSize % 1000 > 0 ? 1 : 0);

            int realCapacity = 0;

            for (int i = 0; i < fetchCounts; i++) {
                int curFetchSize = 1000;
                if (i == fetchCounts - 1 && cacheSize % 1000 > 0) {
                    curFetchSize = cacheSize % 1000;
                }


                List<T> genEles = this.fetchGens(curFetchSize);
                if (Detect.notEmpty(genEles)) {
                    if (i == 0) {
                        gen.resetUsed();//只第一次重置使用量
                    }
                    realCapacity += curFetchSize;
                    gen.setCapacity(realCapacity);//重置总容量

                    genEles.forEach(e -> {
                        gen.putBufEle(e);
                    });

                    monitor.notifyAll();
                }

            }

            if (realCapacity > 0) {
                gen.setReady(true);//设置状态已可用
            }
            monitor.notifyAll();
        }

    }

//    volatile int testInit = 1000;

    private List<T> fetchGens(int fetchSize) {
        // 测试使用
//        List<String> gens = Lists.newArrayList();
//        int cacheSize = this.cacheSize.get();
//        while (true) {
//            if (cacheSize-- > 0) {
//                gens.add(testInit++ + "");
//            } else {
//                break;
//            }
//        }
//
////        System.out.println(gens.size());
//
//        return (List<T>) gens;

        List allGens = Lists.newArrayList();

        int fetchCounts = fetchSize / 1000 + (fetchSize % 1000 > 0 ? 1 : 0);

        String servcie = "distributed-id-service";

        for (int i = 0; i < fetchCounts; i++) {
            int curFetchSize = 1000;
            if (i == fetchCounts - 1 && fetchSize % 1000 > 0) {
                curFetchSize = fetchSize % 1000;
            }

            String api = String.format(GenApiCache.getApi(this.getGenType()), this.getBizTag(), curFetchSize);

            _LOGGER.debug("拉取服务端GEN:{},{}", servcie, api);

            try {

                Future gensFuture = null;

                if (fetchSize == 1) {
                    gensFuture = AsynMicroHttpUtil.get(servcie, api, null, new TypeToken<String>() {
                    });
                    allGens.add(gensFuture.get());
                } else {
                    gensFuture = AsynMicroHttpUtil.get(servcie, api, null, new TypeToken<ArrayList<String>>() {
                    });
                    allGens.addAll((Collection) gensFuture.get());
                }


//                String rr = HttpUtility.sendGet("http://localhost:10246/" + api, null);
//                if (fetchSize == 1) {
//                    allGens.add(rr);
//                } else {
//                    allGens.addAll(JSONObject.parseArray(rr, String.class));
//                }

//            List<String> gens = gensFuture.get();
//            return (List<T>) gens;
            } catch (Exception ex) {
                _LOGGER.error("[获取Gen失败:{}--{}--{}]", servcie, api, ex.getMessage(), ex);
            }

        }

        return allGens;
    }


    public int getNextActivity() {
        int curActivity = activity.get();
        int nextActivity = curActivity == ACTIVITY_PRE ? ACTIVITY_NEXT : ACTIVITY_PRE;

        Gen curGen = gens[curActivity];
//        Gen nextGen = gens[nextActivity];

        if (curGen.isRunning()) {
            return nextActivity;
        }

        return curActivity;
    }

    public boolean isForceFetch() {
        return forceFetch;
    }

    public void setForceFetch(boolean forceFetch) {
        this.forceFetch = forceFetch;
    }
}
